#include "unp.h"
#include <limits.h>
/*
 * int poll(struct pollfd *fds, nfds_t nfds, int timeout);
 */

#define OPEN_MAX FOPEN_MAX  //OPEN_MAX在Linux上不推荐使用，
                            //使用FOPEN_MAX替代
                            //FOPEN_MAX由Ｃ语言定义

static void get_cli_ip(const struct sockaddr_in *cliaddr, void *ip_str, ssize_t len) {
    if (inet_ntop(AF_INET, &cliaddr->sin_addr, ip_str, len) == NULL) {
        perror("inet_ntop");
        exit(EXIT_FAILURE);
    }
}

static void cli_enter(const struct sockaddr_in *cliaddr) {
    char buf[INET_ADDRSTRLEN+1];
    get_cli_ip(cliaddr, buf, INET_ADDRSTRLEN);
    
    printf("client from %s:%d enter\n", buf, ntohs(cliaddr->sin_port));
}

static void cli_exit(int sockfd) {
    char                    buf[INET_ADDRSTRLEN+1];
    socklen_t               clilen;
    struct sockaddr_in      cliaddr;

    clilen= sizeof(cliaddr);
    getpeername(sockfd, (struct sockaddr *)&cliaddr, &clilen);

    get_cli_ip(&cliaddr, buf, INET_ADDRSTRLEN);
    
    printf("client from %s:%d leave\n", buf, ntohs(cliaddr.sin_port));
}

int
main() {
    int                 i, maxi, listenfd, connfd, sockfd;
    int                 nready;
    ssize_t             n;
    char                buf[MAXLINE];
    socklen_t           clilen;
    struct pollfd       client[OPEN_MAX];
    struct sockaddr_in  cliaddr, servaddr;

    if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("openning sock stream");
        exit(1);
    }

    bzero(&servaddr, sizeof(struct sockaddr_in));
    servaddr.sin_family      = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port        = htons(SERV_PORT);

    if (bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
        perror("binding stream socket");
        exit(1);
    }

    if (listen(listenfd, LISTENQ) == -1) {
        perror("listening stream socket");
        exit(1);
    }

    client[0].fd = listenfd;
    client[0].events = POLLRDNORM;
    for (i = 1; i < OPEN_MAX; ++i)
        client[i].fd = -1;  //-1意味着无效的描述符
    maxi = 0;

    //
    for ( ; ; ) {
        nready = poll(client, maxi + 1, -1);

        if (client[0].revents & POLLRDNORM) {
            clilen = sizeof(struct sockaddr_in);
            if ((connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen)) < 0) {
                perror("accept");
                exit(EXIT_FAILURE);
            }

            for (i = 1; i < OPEN_MAX; ++i) 
                if (client[i].fd == -1) {
                    client[i].fd = connfd;
                    break;
                }
            if (i == OPEN_MAX) 
                err_quit("too many clients");

            client[i].events = POLLRDNORM;
            cli_enter(&cliaddr);
            if (connfd > maxi)
                maxi= connfd;
            if (--nready <= 0)
                continue;
        }

        for (i = 1; i < OPEN_MAX; ++i) {
            if ((sockfd = client[i].fd) < 0)
                continue;
            if (client[i].revents & (POLLRDNORM | POLLERR)) {
                if ((n = read(sockfd, buf, MAXLINE)) < 0) {
                    if (errno == ECONNRESET) {//连接被客户重置
                        close(sockfd);
                        client[i].fd = -1;
                        cli_exit(sockfd);
                    } else {
                        err_sys("read error");
                    }
                } else if (n == 0) {//客户端关闭
                    close(sockfd);
                    client[i].fd = -1;
                    cli_exit(sockfd);
                } else 
                    writen(sockfd, buf, n);
                if (--nready <= 0)
                    break;
            }
        }
    }
    return 0;
}
